#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<unistd.h>
#include<strings.h>
#include<pwd.h>
#include<string>
#include<sys/types.h>
#include<sys/wait.h>


using namespace std;
const int basesize=1024;
const int argvnum=64;
const int basesz=1000;
const int envnum=64;
//全局命令行参数表
char* gargv[argvnum];
int gargc=0;
//环境变量表
char* genv[envnum];

char pwd[basesz];//shell的工作路径
//设置成局部变量的话，执行完之后清理缓冲区，会出现问题

char pwdenv[basesize];//全局环境变量
std::string GetUserName()
{
	std::string name=getenv("USER");
	return name.empty()?"None":name;
}

std::string GetHostName()
{
	std::string hostname=getenv("HOSTNAME");
	return hostname.empty()?"None":hostname;
}

std::string GetPwd()
{
//	pwd=getenv("PWD");
	if(nullptr==getcwd(pwd,sizeof(pwd)))
	{
		return "None";	
	}
	snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
	putenv(pwdenv);
//	return pwd.empty()?"None":pwd;
	return pwd;

}

std::string MakeCommandLine()
{
	char command_line[basesize];
	snprintf(command_line,basesize,"[%s@%s %s]#",GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());
	return command_line;
}

void PrintCommand()//1、打印命令行提示符
{
		
	printf("%s",MakeCommandLine().c_str());
	
	//printf("[%s@%s %s]#",GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());
	fflush(stdout);//打印中不带\n，自主刷新缓冲区输出数据
}

bool GetCommand(char command_buffer[],int size)
{
	//输入的命令行=》完整字符串  “ls -a -l”=》是一个字符串
	char* result=fgets(command_buffer,size,stdin);//获取字符时，回车键表示’\n‘;如果等于空，获取失败
	if(!result)
	{
		return false;
	}
	
	if(strlen(command_buffer)==1)
		return false;
	command_buffer[strlen(command_buffer)-1]='\0';//"abc"=>实际长度为4，包含回车键在内，因此要-1
	return true;
}


void ParseCommand(char command_buffer[],int size)
{
	gargc=0;
	memset(gargv,0,sizeof(gargv));
	const char* s=" ";
//	if(size==0)
//		return;
	gargv[gargc++]=strtok(command_buffer,s);//分割字符串，
	while((bool)(gargv[gargc++]=strtok(nullptr,s)));

	//printf("before:%s\n",gargv[gargc]);
	gargc--;
	//gargv[gargc]=(char*)"--color";
	//gargc++;
	//printf("after:%s\n",gargv[gargc]);
}

void debug()
{
	printf("argc:%d\n",gargc);
	int i=0;
	while(gargv[i])
	{
		printf("argv[%d]:%s\n",i,gargv[i]);	
		i++;
	}
}


bool ExecuteCommand()//4、执行命令
{
	pid_t id=fork();
	if(id<0)
	{
		return false;
	}
	else if(id==0)//子进程
	{
		//命令执行
		//1、命令就在命令行参数表里
		//2、调用execvp
		execvpe(gargv[0],gargv,genv);//第一个参数就是要执行的指令
		exit(1);
	}
	else//父进程
	{
		int status=0;
		pid_t rid=waitpid(id,&status,0);
		return true;		
	}
	return false;
}

void Addenv(const char* item)
{
	int i=0;
	while(genv[i])
		i++;
	//为空
	genv[i]=(char*)malloc(strlen(item)+1);
	strncpy(genv[i],item,strlen(item)+1);

	genv[++i]=nullptr;
}

bool CheckExeBuiltCommand()    //检查并执行内建命令
{
	if(strcmp(gargv[0],"cd")==0)
	{
		if(gargc==2)
		{
			chdir(gargv[1]);//进到对应路径	
			
		}
		return true;
	}
	else if(strcmp(gargv[0],"export")==0)
	{
		if(gargc==2)
		{
			Addenv(gargv[1]);	
		}

	}
	else if(strcmp(gargv[0],"env")==0)
	{
		int i=0;
		while(genv[i])
		{
			printf("%s\n",genv[i]);	
			i++;
		}
		return true;
	}
	return false;
}
//从父进程shell中获取环境变量
//本来shell是应该从系统配置获取环境变量
void InitEnv()
{
	extern char** environ;
	int i=0;
	while(environ[i])
	{
		genv[i]=(char*)malloc(strlen(environ[i])+1);
		strncpy(genv[i],environ[i],strlen(environ[i])+1);
		i++;
	}
	genv[i]=nullptr;
}

int main()
{
	InitEnv();
	char command_buffer[basesize];
	while(true)
	{
		PrintCommand();//1、打印命令行提示符
		sleep(1);
		if(!GetCommand(command_buffer,basesize))//2、获取用户命令
		{
			continue;	
		}
//		printf("%s\n",command_buffer);
//
		
		ParseCommand(command_buffer,strlen(command_buffer));//3、分析命令		
//		debug();
		if(CheckExeBuiltCommand())
			continue;
//		else
			ExecuteCommand();//4、执行命令
	}

	return 0;	
}
